package com.zmc.coupon.infrastructure.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lmax.disruptor.RingBuffer;
import com.zmc.common.constant.CacheConstants;
import com.zmc.common.domain.vo.member.MemberInfo;
import com.zmc.common.utils.StringUtils;
import com.zmc.common.utils.redis.RedisCache;
import com.zmc.common.utils.token.AuthTokenService;
import com.zmc.coupon.enums.CouponEnum;
import com.zmc.coupon.infrastructure.bean.query.CouponHistoryQuery;
import com.zmc.coupon.infrastructure.bean.vo.CouponHistoryUpdateProducer;
import com.zmc.coupon.infrastructure.bean.vo.CouponReceiveVo;
import com.zmc.coupon.infrastructure.bean.vo.CouponSkuVo;
import com.zmc.coupon.infrastructure.entity.CouponEntity;
import com.zmc.coupon.infrastructure.entity.CouponSkuEntity;
import com.zmc.coupon.infrastructure.service.CouponService;
import com.zmc.coupon.infrastructure.service.CouponSkuService;
import io.seata.common.util.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zmc.common.utils.PageUtils;

import com.zmc.coupon.infrastructure.dao.CouponHistoryDao;
import com.zmc.coupon.infrastructure.entity.CouponHistoryEntity;
import com.zmc.coupon.infrastructure.service.CouponHistoryService;
import org.springframework.transaction.annotation.Transactional;


/**
 * @author zmc
 */
@Service("couponHistoryService")
public class CouponHistoryServiceImpl extends ServiceImpl<CouponHistoryDao, CouponHistoryEntity> implements CouponHistoryService {

    @Autowired
    private AuthTokenService authTokenService;

    @Autowired
    private CouponService couponService;

    @Autowired
    private CouponSkuService couponSkuService;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private RingBuffer<CouponHistoryUpdateProducer> couponHistoryUpdateRingBuffer;

    @Override
    public PageUtils queryPage(CouponHistoryQuery query) {
        Page<CouponHistoryEntity> page = baseMapper.selectPage(new Page<>(query.getPageNum(), query.getPageSize()), getWrapper(query));
        List<Long> couponIds = page.getRecords().stream().map(CouponHistoryEntity::getCouponId).collect(Collectors.toList());
        //获取优惠卷信息
        if (query.getType() == 1 && CollectionUtils.isNotEmpty(couponIds)) {
            List<CouponEntity> couponList = couponService.listByIds(couponIds);
            //优惠卷分类
            Map<Integer, List<CouponEntity>> map = couponList.stream().collect(Collectors.groupingBy(CouponEntity::getUseType));
            for (Map.Entry<Integer, List<CouponEntity>> entry : map.entrySet()) {
                setCouponCategory(page.getRecords(),entry.getValue());
            }
        }
        //第二次过滤检查(剔除将要被更新的数据，即useType = 0, 但是检查发现卷记录已过期，需更新useType = 2)
        if (query.getUseType() == 0) {
            List<CouponHistoryEntity> collect = page.getRecords().stream().filter(item -> Objects.equals(query.getUseType(), item.getUseType())).collect(Collectors.toList());
            if (collect.size() != page.getRecords().size()) {
                page.setRecords(collect);
                page.setTotal(collect.size());
            }
        }
        return new PageUtils(page);
    }

    @Override
    public List<CouponHistoryEntity> queryList(CouponHistoryQuery query) {
        return list(getWrapper(query));
    }

    @Override
    public List<CouponHistoryEntity> getUserHistoryList(List<Long> couponIds) {
        LambdaQueryWrapper<CouponHistoryEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(CouponHistoryEntity::getCouponId, couponIds)
                .eq(CouponHistoryEntity::getMemberId, authTokenService.getMemberInfo().getMemberId());
        return list(wrapper);
    }

    @Override
    public List<CouponHistoryEntity> getCouponHistory(CouponReceiveVo vo) {
        LambdaQueryWrapper<CouponHistoryEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CouponHistoryEntity::getCouponId, vo.getCouponId())
                .eq(CouponHistoryEntity::getSkuId, vo.getSkuId())
                .eq(CouponHistoryEntity::getMemberId, authTokenService.getMemberInfo().getMemberId());
        return list(wrapper);
    }

    @Override
    @Transactional
    public void createCouponHistory(CouponReceiveVo vo) {
        CouponHistoryEntity entity = new CouponHistoryEntity();
        MemberInfo memberInfo = authTokenService.getMemberInfo();
        entity.setCouponId(vo.getCouponId());
        entity.setSkuId(vo.getSkuId());
        entity.setMemberId(memberInfo.getMemberId());
        entity.setMemberNickName(memberInfo.getNickname());
        entity.setGetType(CouponEnum.COUPON_GET_TYPE_GET.getCode());
        entity.setUseType(CouponEnum.COUPON_USE_TYPE_NOT_USE.getCode());
        save(entity);
    }

    @Override
    public List<CouponHistoryEntity> listByOrderSn(String orderSn) {
        LambdaQueryWrapper<CouponHistoryEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CouponHistoryEntity::getOrderSn, orderSn);
        return list(wrapper);
    }

    @Override
    public void updateByOrderSn(String orderSn) {
        baseMapper.updateByOrderSn(orderSn);
    }

    @Override
    public void updateExpiredByCouponId(Long couponId) {
        String key = CacheConstants.COUPON_HISTORY_EXPIRE_PREFIX + couponId;
        boolean lock = redisCache.setCacheIfAbsent(key, "true", 30L, TimeUnit.SECONDS);
        if (lock) {
           try{
               LambdaQueryWrapper<CouponHistoryEntity> wrapper = new LambdaQueryWrapper<>();
               wrapper.eq(CouponHistoryEntity::getCouponId, couponId).last(" limit 1");
               CouponHistoryEntity one = getOne(wrapper);
               if (one.getUseType() == CouponEnum.COUPON_USE_TYPE_NOT_USE.getCode()) {
                   baseMapper.updateExpiredByCouponId(couponId);
               }
           } finally {
               redisCache.deleteObject(key);
           }
        }
    }

    public LambdaQueryWrapper<CouponHistoryEntity> getWrapper(CouponHistoryQuery query) {
        LambdaQueryWrapper<CouponHistoryEntity> wrapper = new LambdaQueryWrapper<>();
        if (query.getType() == 1) {
            wrapper.eq(CouponHistoryEntity::getMemberId, authTokenService.getMemberInfo().getMemberId())
                    .eq(!Objects.isNull(query.getUseType()), CouponHistoryEntity::getUseType, query.getUseType());
        } else {
            wrapper.eq(StringUtils.isNotEmpty(query.getMemberNickName()), CouponHistoryEntity::getMemberNickName, query.getMemberNickName())
                    .eq(!Objects.isNull(query.getUseType()), CouponHistoryEntity::getUseType, query.getUseType())
                    .eq(!Objects.isNull(query.getOrderSn()), CouponHistoryEntity::getOrderSn, query.getOrderSn())
                    .ge(Objects.isNull(query.getBeginTime()), CouponHistoryEntity::getCreateTime, query.getBeginTime())
                    .le(Objects.isNull(query.getEndTime()), CouponHistoryEntity::getCreateTime, query.getEndTime())
                    .orderByDesc(CouponHistoryEntity::getCreateTime);
        }
        return wrapper;
    }

    /**
     * 设置优惠卷详情信息
     */
    public void setCouponCategory(List<CouponHistoryEntity> records, List<CouponEntity> list) {
        List<CouponSkuVo> collect = records.stream().map(item -> new CouponSkuVo(item.getCouponId(), item.getSkuId())).collect(Collectors.toList());
        List<CouponSkuEntity> spuList = couponSkuService.getByCouponIdAndSkuId(collect);
        LocalDateTime now = LocalDateTime.now();
        if (CollectionUtils.isNotEmpty(spuList)) {
            records.forEach(item -> {
                spuList.stream().filter(spu -> Objects.equals(spu.getCouponId(), item.getCouponId()) && Objects.equals(spu.getSkuId(), item.getSkuId())).findFirst().ifPresent(item::setCouponSpu);
                list.stream().filter(coupon -> Objects.equals(coupon.getId(), item.getCouponId())).findFirst()
                        .ifPresent(coupon -> {
                            item.setCoupon(coupon);
                            //若优惠卷已过期，但未更新，异步更新
                            if (now.isAfter(coupon.getEndTime()) && item.getUseType() == CouponEnum.COUPON_USE_TYPE_NOT_USE.getCode()) {
                                item.setUseType(CouponEnum.COUPON_USE_TYPE_EXPIRE.getCode());
                                new CouponHistoryUpdateProducer(couponHistoryUpdateRingBuffer, item.getCouponId()).publish();
                            }
                        });
            });
        }

    }

}